AWS CLIの実装からEC2 Instance Connect Endpointを読み解いてみた
しばたです。
先日EC2 Instance Connectの新機能となるEC2 Instance Connect Endpointが公開されDevelopersIOでもいくつか記事が公開されています。
- [新サービス] Amazon EC2 Instance Connect Endpoint が発表されました
- EC2 Instance Connect エンドポイント登場!パブリックIPのないEC2にSSH・RDPできるようになりました
- [アップデート]パブリック IP アドレスなしで、EC2インスタンスにSSH接続できる EC2 Instance Connect Endpointがリリースしました
- EC2 Instance Connect Endpoint と Session Managerの違いをまとめてみた
基本的な機能や使い方についてはこれらの記事をご覧いただくと良いでしょう。
ただ、私個人としてはこの機能の実装や従来のEC2 Instance Connectとの違いが気になりいろいろ調べてみたのでその結果を共有したいと思います。
EC2 Instance Connect Endpoint概要
改めてこのEC2 Instance Connect Endpointの概要を解説しておきます。
サービスの構成としては下図の様になり、ユーザーVPC内にVPC Endpointを作成することで「AWS外部からEC2 Instance Connect Endpoint Service → VPC Endpoint → 対象EC2インスタンス」の経路で接続可能になります。
(https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Connect-using-EC2-Instance-Connect-Endpoint.html
より引用)
VPC Endpointが通信の抜け道となることでPrivateなサブネットに対してもSSH接続できる様になるわけです。
従来のEC2 Instance Connectとの違い
従来のEC2 Instance ConnectはPublicに公開されたLinux EC2インスタンスに対しIAMの権限でアクセス制御を行うものであり通信経路に関する設定は一切出てきません。
専用のエージェントプログラムを使いインスタンスメタデータを介したSSH鍵の連携がメイン機能となっています。
サービスとしてはどちらもEC2 Instance Connectに属しますが内容は全くの別物です。
aws ec2-instance-connect open-tunnel コマンドとその実装
ドキュメントによればAWS CLIでEC2 Instance Connect Endpointを利用する際はaws ec2-instance-connect open-tunnel
コマンドを使うとされています。
# OpenSSHのProxyコマンドとして利用する場合の実行例
ssh -i my-key-pair.pem ec2-user@i-0123456789example \
-o ProxyCommand='aws ec2-instance-connect open-tunnel --instance-id i-0123456789example'
本日(2023年6月14日)時点ではまだaws ec2-instance-connect open-tunnel
コマンドを含むバージョンは未提供であり動作確認することはできませんでした。
しかしながらGitHubではAWS CLI v2向けにこのコマンドを追加するプルリクエストがマージされておりソースコードを読むことはできたので、ソースコードからこの機能の実装を読み解いていきます。
aws ec2-instance-connect open-tunnel
コマンドは単純なAWS REST APIのラッパーではなくAWS CLI独自のカスタマイズがされています。
このコマンドではざっくり以下の作業を行っています。
- 指定されたEC2インスタンスIDから属するVPC/サブネット情報およびPrivate IPアドレスを取得
- VPC EndpointのDNS名を取得
- VPC Endpoint IDが指定されている場合はVPC Endpoint情報を直接取得
- VPC Endpoint IDが指定されていない場合はEC2のVPC/サブネット情報から探索する (
eicefetcher.py
)
- VPC EndpointのDNS名をベースにWebSocketのPre-signed URLを発行する (
eicesigner.py
,websocket.py
)- Pre-signed URLのパラメーターで宛先EC2インスタンスのPrivate IPとポート番号を指定
- WebSocketトンネルの有効期間(max_tunnel_duration)は最長3600秒
- Pre-signed URL自体の有効期間(expires_in)は60秒
- 発行されたPre-singed URLに接続しEC2インスタンス用のトンネルを張る
open-tunnel
の名前の通りWebSocketトンネルを張るまでがこのコマンドの仕事になっています。
このため前述のコマンド例でssh -i my-key-pair.pem
とある様に実際にEC2インスタンスに接続する際は鍵指定が必要となっています。
また、REST APIに直接的なOpenTunnel
というアクションがあるわけではなく、WebSocketトンネルを張って接続した際にCloudTrailにイベントが記録される様です。[1]
例えばセキュリティグループ設定の不備などにより接続失敗した場合
{
// ・・・省略・・・
"eventSource": "ec2-instance-connect.amazonaws.com",
"eventName": "OpenTunnel",
// ・・・省略・・・
"errorCode": "DialFailure",
"errorMessage": "Unable to dial target private ip",
// ・・・省略・・・
}
といったイベントが記録されていました。
ちなみに接続成功した場合はエラーメッセージ無しのイベントが記録されます。
余談
AWS CLIの実装を調べてみた結果はこんな感じでした。
他にいくつか余談があるので簡単に補足しておきます。
1. マネジメントコンソールから接続する場合の処理
マネジメントコンソールからEC2 Instance Connect Endpointを使った接続をする際はSSH鍵(キーペア)を指定する必要がありません。
(マネジメントコンソールのUIではキーペアの指定欄が無い)
マネジメントコンソールはその実装を読むことが出来ないためCloudTrailイベントからの推測になりますが、どうやら従来のEC2 Instance Connectの機能(SSH鍵の自動生成+連携)とWebSocketトンネルを張るのを併用している様です。
- SendSSHPublicKeyイベント (都度ED25519の公開鍵が生成されインスタンスに送られていた)
- OpenTunnelイベント (インスタンスに対してトンネルが張られていた)
の順でイベントが記録されていました。
加えてEC2インスタンスにインストールされているEC2 Instance Connect Agentをアンインストールすると接続に失敗する様になったので、この挙動からも両者を併用していると言えるでしょう。
2. aws ec2-instance-connect ssh コマンド
先述のプルリクエストでは新たにaws ec2-instance-connect ssh
コマンドも増える見込みです。
このコマンドは内部でaws ec2-instance-connect send-ssh-public-key
およびaws ec2-instance-connect open-tunnel
コマンド相当の機能を実行したうえでSSH接続を行ってくれる便利なラッパーコマンドになっています。
ざっくり前項のマネジメントコンソールでの接続と同等の機能を提供してくれるものと考えると分かりやすいでしょう。
(実際にはもっと細かいパラメーター指定が可能な模様)
# WebSocketトンネルを張った上でSSH接続する例
aws ec2-instance-connect ssh --instance-id i-1234567890example --connection-type eice
リリース前なので実際に試せていませんが便利に使えそうな予感がします。
3. 最長トンネル期間を超えた場合
最長トンネル期間(max_tunnel_duration)を超えた場合WebSocketの接続は閉じられます。
マネジメントコンソールからのアクセスで動作確認した際はエラーメッセージ等が出る事無く処理がハングしました。
AWS CLIではエラーメッセージを出力する様に読み取れました。
ちなみに期間延長の仕組みは無い様で、期間を超えた場合は都度接続し直す必要がありそうです。
最後に
以上となります。
改めて本記事の内容をまとめると
- 従来のEC2 Instance Connectの機能はSSH鍵の自動生成+連携がメインである (
SendSSHPublicKey
) - 今回のEC2 Instance Connect EndpointはEC2インスタンスに対してWebSocketトンネルを張る機能である (
OpenTunnel
) - 両者は別の機能であり併用することも可能である
であることが分かりました。
まだ実際にAWS CLIのコマンドを試すことができず、そのため本記事の内容に誤りがあるかもしれませんのでその点はご了承ください。
新しいバージョンのAWS CLIがリリースされたら実際に動作確認して検証したいと思います。
ただし、IAM Policyの制御においては ec2-instance-connect:OpenTunnel というアクションが存在します。 ↩︎